// Filename: frustum_src.I
// Created by:  mike (09Jan97)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University.  All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license.  You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////
//     Function: Constructor
//       Access:
//  Description:
////////////////////////////////////////////////////////////////////
INLINE_MATHUTIL FLOATNAME(LFrustum)::
FLOATNAME(LFrustum)() {
  _fnear = FLOATCONST(1.4142);
  _ffar = FLOATCONST(10.0);
  _l = -1.0f;
  _r = 1.0f;
  _t = 1.0f;
  _b = -1.0f;
}

////////////////////////////////////////////////////////////////////
//     Function: make_ortho_2D
//       Access:
//  Description: Sets up a two-dimensional orthographic frustum
////////////////////////////////////////////////////////////////////
INLINE_MATHUTIL void FLOATNAME(LFrustum)::make_ortho_2D() {
  make_ortho(-1.0f, 1.0f);
}

////////////////////////////////////////////////////////////////////
//     Function: make_ortho_2D
//       Access:
//  Description: Sets up a two-dimensional orthographic frustum
////////////////////////////////////////////////////////////////////
INLINE_MATHUTIL void FLOATNAME(LFrustum)::
make_ortho_2D(FLOATTYPE l, FLOATTYPE r, FLOATTYPE t, FLOATTYPE b) {
  make_ortho(-1.0f, 1.0f, l, r, t, b);
}

////////////////////////////////////////////////////////////////////
//     Function: make_ortho_2D
//       Access:
//  Description: Behaves like gluOrtho
////////////////////////////////////////////////////////////////////
INLINE_MATHUTIL void FLOATNAME(LFrustum)::make_ortho(FLOATTYPE fnear, FLOATTYPE ffar) {
  _fnear = fnear;
  _ffar = ffar;
  _l = -1.0f;
  _r = 1.0f;
  _t = 1.0f;
  _b = -1.0f;
}

////////////////////////////////////////////////////////////////////
//     Function: make_ortho_2D
//       Access:
//  Description: Behaves like gluOrtho
////////////////////////////////////////////////////////////////////
INLINE_MATHUTIL void FLOATNAME(LFrustum)::
make_ortho(FLOATTYPE fnear, FLOATTYPE ffar, FLOATTYPE l, FLOATTYPE r,
           FLOATTYPE t, FLOATTYPE b) {
  _fnear = fnear;
  _ffar = ffar;
  _l = l;
  _r = r;
  _t = t;
  _b = b;
}

////////////////////////////////////////////////////////////////////
//     Function: make_perspective
//       Access:
//  Description: Behaves like gluPerspective (Aspect = width/height,
//               Yfov in degrees)
//       aspect
//   +------------+
//   |            |
// 1 |            | yfov
//   |            |
//   +------------+
//
//     -------+------
//      \     |     /
//       \    |    /
//        \   |   /
//         \  |  /
//          \ | /
//           \|/
//            W yfov
//
////////////////////////////////////////////////////////////////////
INLINE_MATHUTIL void FLOATNAME(LFrustum)::
make_perspective_hfov(FLOATTYPE hfov, FLOATTYPE aspect, FLOATTYPE fnear,
                      FLOATTYPE ffar) {
  _fnear = fnear;
  _ffar = ffar;
  _r = tan(deg_2_rad(hfov) * FLOATCONST(0.5)) * _fnear;
  _l = -_r;
  _t = _r / aspect;
  _b = -_t;
}


INLINE_MATHUTIL void FLOATNAME(LFrustum)::
make_perspective_vfov(FLOATTYPE yfov, FLOATTYPE aspect, FLOATTYPE fnear,
                      FLOATTYPE ffar) {
  _fnear = fnear;
  _ffar = ffar;
  _t = tan(deg_2_rad(yfov) * 0.5f) * _fnear;
  _b = -_t;
  _r = _t * aspect;
  _l = -_r;
}


INLINE_MATHUTIL void FLOATNAME(LFrustum)::
make_perspective(FLOATTYPE xfov, FLOATTYPE yfov, FLOATTYPE fnear,
                 FLOATTYPE ffar) {
  _fnear = fnear;
  _ffar = ffar;
  _t = tan(deg_2_rad(yfov) * 0.5f) * _fnear;
  _b = -_t;
  _r = tan(deg_2_rad(xfov) * 0.5f) * _fnear;
  _l = -_r;
}

////////////////////////////////////////////////////////////////////
//     Function: get_perspective_params
//       Access:
//  Description:
////////////////////////////////////////////////////////////////////
INLINE_MATHUTIL void FLOATNAME(LFrustum)::
get_perspective_params(FLOATTYPE& yfov, FLOATTYPE& aspect,
                       FLOATTYPE& fnear, FLOATTYPE& ffar) const {
  yfov = rad_2_deg(atan(_t / _fnear)) * 2.0f;
  aspect = _r / _t;
  fnear = _fnear;
  ffar = _ffar;
}

////////////////////////////////////////////////////////////////////
//     Function: get_perspective_params
//       Access:
//  Description:
////////////////////////////////////////////////////////////////////
INLINE_MATHUTIL void FLOATNAME(LFrustum)::
get_perspective_params(FLOATTYPE& xfov, FLOATTYPE& yfov, FLOATTYPE& aspect,
                       FLOATTYPE& fnear, FLOATTYPE& ffar) const {
  xfov = rad_2_deg(atan(_r / _fnear)) * 2.0f;
  get_perspective_params(yfov, aspect, fnear, ffar);
}

////////////////////////////////////////////////////////////////////
//     Function: get_perspective_projection_mat
//       Access: Public
//  Description: This computes a transform matrix that performs the
//               perspective transform defined by the frustum,
//               accordinate to the indicated coordinate system.
////////////////////////////////////////////////////////////////////
INLINE_MATHUTIL FLOATNAME(LMatrix4) FLOATNAME(LFrustum)::
get_perspective_projection_mat(CoordinateSystem cs) const {
  if (cs == CS_default) {
    cs = get_default_coordinate_system();
  }

  FLOATTYPE recip_far_minus_near = 1.0f/(_ffar - _fnear);
  FLOATTYPE recip_r_minus_l = 1.0f/(_r - _l);
  FLOATTYPE recip_t_minus_b = 1.0f/(_t - _b);
  FLOATTYPE two_fnear = 2.0f*_fnear;

  FLOATTYPE d = (_r + _l) * recip_r_minus_l;
  FLOATTYPE a = two_fnear * recip_r_minus_l;
  FLOATTYPE e = two_fnear * recip_t_minus_b;
  FLOATTYPE b = (_t + _b) * recip_t_minus_b;
  FLOATTYPE c = (_ffar + _fnear) * recip_far_minus_near;
  FLOATTYPE f = -_ffar * two_fnear * recip_far_minus_near;

/*
  FLOATTYPE a = (2.0f * _fnear) / (_r - _l);
  FLOATTYPE b = (_t + _b) / (_t - _b);
  FLOATTYPE c = (_ffar + _fnear) / (_ffar - _fnear);
  FLOATTYPE d = (_r + _l) / (_r - _l);
  FLOATTYPE e = (2.0f * _fnear) / (_t - _b);
  FLOATTYPE f = (-2.0f * _ffar * _fnear) / (_ffar - _fnear);
*/

  switch (cs) {
  case CS_zup_right:
    return FLOATNAME(LMatrix4)(  a, 0.0f, 0.0f, 0.0f,
                               0.0f,  -b,   c, 1.0f,
                                 d,   e, 0.0f, 0.0f,
                               0.0f, 0.0f,   f, 0.0f);

  case CS_yup_right:
    return FLOATNAME(LMatrix4)(  a, 0.0f, 0.0f, 0.0f,
                               0.0f,   e, 0.0f, 0.0f,
                                 d,   b,  -c,-1.0f,
                               0.0f, 0.0f,   f, 0.0f);

  case CS_zup_left:
    return FLOATNAME(LMatrix4)::convert_mat(CS_zup_right, CS_zup_left) *
      get_perspective_projection_mat(CS_zup_right);

  case CS_yup_left:
    return FLOATNAME(LMatrix4)::convert_mat(CS_yup_right, CS_yup_left) *
      get_perspective_projection_mat(CS_yup_right);

  default:
    mathutil_cat.error()
      << "Invalid coordinate system!\n";
    return FLOATNAME(LMatrix4)::ident_mat();
  }
}

////////////////////////////////////////////////////////////////////
//     Function: get_ortho_projection_mat
//       Access: Public
//  Description: This computes a transform matrix that performs the
//               orthographic transform defined by the frustum,
//               accordinate to the indicated coordinate system.
////////////////////////////////////////////////////////////////////
INLINE_MATHUTIL FLOATNAME(LMatrix4) FLOATNAME(LFrustum)::
get_ortho_projection_mat(CoordinateSystem cs) const {
  if (cs == CS_default) {
    cs = get_default_coordinate_system();
  }

  FLOATTYPE a = 2.0f / (_r - _l);
  FLOATTYPE b = 2.0f / (_t - _b);
  FLOATTYPE c = 2.0f / (_ffar - _fnear);
  FLOATTYPE d = (_r + _l) * a * 0.5f;
  FLOATTYPE e = (_t + _b) * b * 0.5f;
  FLOATTYPE f = (_ffar + _fnear) * c * 0.5f;

/*
  FLOATTYPE a = 2.0f / (_r - _l);
  FLOATTYPE b = 2.0f / (_t - _b);
  FLOATTYPE c = 2.0f / (_ffar - _fnear);
  FLOATTYPE d = (_r + _l) / (_r + _l)
  FLOATTYPE e = (_t + _b) / (_t - _b);
  FLOATTYPE f = (_ffar + _fnear) / (_ffar - _fnear);
*/
  switch (cs) {
  case CS_zup_right:
    return FLOATNAME(LMatrix4)::convert_mat(CS_yup_right, CS_zup_right) *
      get_ortho_projection_mat(CS_yup_right);

  case CS_yup_right:
    return FLOATNAME(LMatrix4)(  a, 0.0f, 0.0f, 0.0f,
                               0.0f,   b, 0.0f, 0.0f,
                               0.0f, 0.0f,  -c, 0.0f,
                                -d,  -e,  -f, 1.0f);

  case CS_zup_left:
    return FLOATNAME(LMatrix4)::convert_mat(CS_zup_right, CS_zup_left) *
      get_ortho_projection_mat(CS_zup_right);

  case CS_yup_left:
    return FLOATNAME(LMatrix4)::convert_mat(CS_yup_right, CS_yup_left) *
      get_ortho_projection_mat(CS_yup_right);

  default:
    mathutil_cat.error()
      << "Invalid coordinate system!\n";
    return FLOATNAME(LMatrix4)::ident_mat();
  }
}
